


import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Random;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.Vertex;
import edu.uci.ics.jung.io.GraphMLFile;

public class Simulator {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		Graph taskGraph = null;
		
		//Input:
		//The simulator takes as input the application consisting of an interacting set of tasks stored
		//in a GraphML file and the simulation time.

		//Get information about the tasks
		ArrayList<Task> tasks = new ArrayList<Task>();
		ArrayList<Integer> taskPeriods = new ArrayList<Integer>();
		
		try {
			taskGraph = getTaskGraph("Casestudy1/taskgraph_1.graphml");
		} catch (IOException e) {
			
			e.printStackTrace();
		}
		
		for (@SuppressWarnings("unchecked")
		Iterator<Object> iter = taskGraph.getVertices().iterator(); iter.hasNext();) {
			Vertex v = (Vertex) iter.next();

			int taskPeriod = getPeriodValue(v);

			tasks.add(new Task(taskPeriod, getBCETValue(v), getWCETValue(v)));
			taskPeriods.add(taskPeriod);
		}
		
		int lcm = 1;
		for(Task task : tasks)
		{
			lcm = getLCM(task.getPeriod(), lcm);
		}
		
		for(Task task : tasks)
		{
			task.setPriority(lcm - lcm / task.getPeriod());
		}
		
		int numberOfCycles = 1 * lcm;
		
		ArrayList<Job> jobs = new ArrayList<Job>();
		
		for(Task task : tasks)
		{
			for(int i = 0; i < numberOfCycles / task.getPeriod(); i++)
			{
				Job job = new Job();
				job.setRelease(task.getPeriod() * i);
				job.setTime(getRandomInteger(task.getBcet(), task.getWcet(), rand));
				//job.setTime((int)(Math.random() * (task.getWcet() - task.getBcet()) * 1.0) + task.getBcet()); //Do a random method.
				job.setInitTime(job.getTime());
				task.getJobs().add(job);
				job.setTask(task);
				jobs.add(job);
			}
		}
		
		
		//Simulate
		Collections.sort(jobs, new JobComparator());
				
		for(int i = 0; i < numberOfCycles; i++) {
			Job currentJob = null;
			for(Job job : jobs){
				if(job.getRelease() <= i && (currentJob == null || compareJobs(job, currentJob))) {
					currentJob = job;
				}
			}
					
			if(currentJob != null){
				
				if(currentJob.getStart() == -1){
					currentJob.setStart(i);
				}
				
				if(currentJob.getTime() != 0) {
					currentJob.setTime(currentJob.getTime() - 1);
				}
				
				if(currentJob.getTime() == 0) {
					
					currentJob.setResponse(i - currentJob.getStart());
				
				if(currentJob.getTask().getWcrt() < currentJob.getResponse()) {
								
					currentJob.getTask().setWcrt(currentJob.getResponse());
				}
							
				jobs.remove(currentJob);
				}	
			}
		}
				
		for(Task task : tasks) {
					
			System.out.println(task.toString());
			for(Job job : task.getJobs()) {
						
				System.out.println(job.toString());
			}
		}
		
		
		
	}

	// if j1 has a higher priority than j2
		private static boolean compareJobs(Job j1, Job j2)
		{
			if(j1.getTask().getPriority() < j2.getTask().getPriority())
			{
				return true;
			}
			
			if(j1.getTask().getPriority() == j2.getTask().getPriority())
			{
				if(j1.getTask().getName().compareToIgnoreCase(j2.getTask().getName()) > 0)
				{
					return true;
				}
				if(j1.getTask().getName().compareToIgnoreCase(j2.getTask().getName()) == 0)
				{
					throw new RuntimeException("Two tasks have the same name and period! Are they the same one task?..");
				}
			}
			
			return false;
		}
	
	/**
	 * Reads a task: in this case, reads it from the file "taskgraph.graphml"
	 * 
	 * @return A undirected graph containing the tasks, their computation times
	 *         periods and respective deadlines.
	 */

	public static Graph getTaskGraph(String input) throws IOException {
		GraphMLFile gml = new GraphMLFile();

		Graph g_TG = gml.load(input);

		return g_TG;
	}
	
	/**
	 * Returns the WCET Value of the Task Node.
	 * 
	 * It converts the exisiting UserDatum Value to integer
	 * 
	 * @return An Int
	 */
	public static int getWCETValue(Vertex v) {
		return Integer.parseInt((v.getUserDatum("WCET")).toString());
	}
	
	/**
	 * Returns the Period Value of the Task Node.
	 * 
	 * It converts the exisiting UserDatum Value to integer
	 * 
	 * @return An Int
	 */
	public static int getPeriodValue(Vertex v) {
		return Integer.parseInt((v.getUserDatum("Period")).toString());
	}
	
	/**
	 * Returns the BCET Value of the Task Node.
	 * 
	 * It converts the exisiting UserDatum Value to integer
	 * 
	 * @return An Int
	 */
	public static int getBCETValue(Vertex v) {
		return Integer.parseInt((v.getUserDatum("BCET")).toString());
	}
	
	/**
	 * Returns a random number generated from a range of numbers. This 
	 * range is defined by a start number A and a end number B.
	 * 
	 * 
	 */
	public static int getRandomInteger(int start, int end, Random rand){
		
		if ( start > end ) {
		      throw new IllegalArgumentException("Start cannot exceed End.");
		}
		
		//get the range, casting to long to avoid overflow problems
	    long range = (long)end - (long)start + 1;
	    // compute a fraction of the range, 0 <= frac < range
		
	    long fraction = (long)(range * rand.nextDouble());
		
	    int randomNumber =  (int)(fraction + start);    
		
	    return randomNumber;
		  
	}
	
	public static int getLCM(int a, int b)
	{
		return (a * b) / getGCD(a, b);
	}
	
	public static int getGCD(int a, int b)
	{
		int max = Math.max(a, b);
		int min = Math.min(a, b);
		
		if(min == 0)
		{
			return max;
		}
		
		return getGCD(min, max % min);
	}
}
